package org.JWCB.friend.service.user;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.JWCB.api.domain.judge.dto.JudgeSubmitDto;
import org.JWCB.api.domain.judge.vo.UserExeResItem;
import org.JWCB.api.service.judge.RemoteJudgeService;
import org.JWCB.common.core.constants.CacheConstants;
import org.JWCB.common.core.constants.UserConstants;
import org.JWCB.common.core.domain.vo.R;
import org.JWCB.common.core.enums.ProgramType;
import org.JWCB.common.core.enums.ResultCode;
import org.JWCB.common.core.enums.SubmitResType;
import org.JWCB.common.core.exception.ServiceException;

import org.JWCB.common.core.utils.ThreadLocalUtil;
import org.JWCB.common.redis.service.RedisService;
import org.JWCB.friend.aspect.CheckUserStatus;
import org.JWCB.friend.domain.question.entity.Question;
import org.JWCB.friend.domain.question.es.QuestionES;
import org.JWCB.friend.domain.question.vo.QuestionCase;
import org.JWCB.friend.domain.user.dto.UserSubmitDto;
import org.JWCB.api.domain.judge.vo.UserQuestionResVo;
import org.JWCB.friend.domain.user.entity.UserSubmit;
import org.JWCB.friend.elasticsearch.QuestionRepository;
import org.JWCB.friend.mapper.question.QuestionMapper;
import org.JWCB.friend.mapper.user.UserSubmitMapper;
import org.JWCB.friend.rabbitmq.JudgeProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserQuestionService {



    @Autowired
    private JudgeProducer judgeProducer;
    @Autowired
    private RemoteJudgeService remoteJudgeService;
    @Autowired
    private QuestionRepository questionRepository;
    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Value("${submit.submitExp:60}")
    private Long submitExp;

    @Value("${submit.submitCount:1}")
    private Long submitCount;


    @Autowired
    private RedisService redisService;
    /**
     * 用户提交代码
     */
    @CheckUserStatus
    public R<UserQuestionResVo> submit(UserSubmitDto userSubmitDto) {
        Integer programType = userSubmitDto.getProgramType();
        if(ProgramType.JAVA.getValue().equals(programType)) {
            return javaSubmit(userSubmitDto);
        }else {
            // 待开发其他语言类型
            throw new ServiceException(ResultCode.FAILED_NOT_SUPPORT_PROGRAM_TYPE);
        }
    }


    /**
     * 判断上传代码限制次数
     */
    private void checkSubmitCount() {
        Long userId = ThreadLocalUtil.get(UserConstants.USER_ID,Long.class);
        String submitKey = getSubmitKey(userId);
        Long count = redisService.getCacheObject(submitKey,Long.class);
        if(count == null) {
            redisService.setCacheObject(submitKey,1,submitExp, TimeUnit.SECONDS);
        }else {
            if(count >= submitCount) {
                // 超出限制
                throw new ServiceException(ResultCode.SUBMIT_MAX_COUNT);
            }else {
                redisService.increment(submitKey);
            }
        }
    }


    /**
     * 获取提交限制缓存key
     */
    private String getSubmitKey(Long userId) {
        return CacheConstants.USER_SUBMIT_COUNT_KEY + userId;
    }
    /**
     * 用户提交代码 -- mq版本
     */
    @CheckUserStatus

    public String mQSubmit(UserSubmitDto userSubmitDto) {
        checkSubmitCount();
        Integer programType = userSubmitDto.getProgramType();
        if(ProgramType.JAVA.getValue().equals(programType)) {
            return mqJavaSubmit(userSubmitDto);
        }else {
            // 待开发其他语言类型
            throw new ServiceException(ResultCode.FAILED_NOT_SUPPORT_PROGRAM_TYPE);
        }
    }


    /**
     * java代码提交
     */
    private R<UserQuestionResVo> javaSubmit(UserSubmitDto userSubmitDto) {
        JudgeSubmitDto judgeSubmitDto = assembleJavaJudgeSubmitDto(userSubmitDto);
        return remoteJudgeService.doJudgeJavaCode(judgeSubmitDto);
    }


    /**
     * java代码提交 --mq版本
     */
    private String mqJavaSubmit(UserSubmitDto userSubmitDto) {
        String uniqueId = UUID.fastUUID().toString();
        userSubmitDto.setUniqueId(uniqueId);
        JudgeSubmitDto judgeSubmitDto = assembleJavaJudgeSubmitDto(userSubmitDto);
        judgeProducer.produceJudgeMsg(judgeSubmitDto);
        return uniqueId;
//        return remoteJudgeService.doJudgeJavaCode(judgeSubmitDto);
    }

    /**
     * 拼装判题dto
     */
    private JudgeSubmitDto assembleJavaJudgeSubmitDto(UserSubmitDto userSubmitDto) {
        Long questionId = userSubmitDto.getQuestionId();
        JudgeSubmitDto judgeSubmitDto = new JudgeSubmitDto();
        QuestionES questionES = questionRepository.findById(questionId).orElse(null);
        if(questionES != null) {
            BeanUtil.copyProperties(questionES,judgeSubmitDto);
        }else {
            log.info("{}题目数据缺失... 重新查询mysql",questionId);
            Question question = questionMapper.selectById(questionId);
            if(question == null) {
                log.error("{} 获取题目{} 失败!!",getUserId(),questionId);
                throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
            }
            BeanUtil.copyProperties(question,judgeSubmitDto);
            questionES = new QuestionES();
            BeanUtil.copyProperties(question,questionES);
            questionRepository.save(questionES);
        }
        BeanUtil.copyProperties(userSubmitDto,judgeSubmitDto);
        judgeSubmitDto.setUserId(getUserId());
        judgeSubmitDto.setUserCode(assembleJavaCode(userSubmitDto.getUserCode(),questionES.getMainFunc()));
        List<QuestionCase>  questionCases = JSONUtil.toList(questionES.getQuestionCase(), QuestionCase.class);
        List<String> inputs = questionCases.stream().map(QuestionCase::getInput).toList();
        List<String> outputs = questionCases.stream().map(QuestionCase::getOutput).toList();
        judgeSubmitDto.setInputList(inputs);
        judgeSubmitDto.setOutputList(outputs);
        return judgeSubmitDto;
    }

    /**
     * 获取userId -> ThreadLocal
     */
    private Long getUserId() {
        Long userId = ThreadLocalUtil.get(UserConstants.USER_ID, Long.class);
        if(userId == null) {
            log.error("获取用户id失败");
            throw new ServiceException(ResultCode.FAILED);
        }
        return userId;
    }


    /**
     * 拼接java代码
     */
    private String assembleJavaCode(String userCode, String mainFunc) {
        String targetCh = "}";
        int targetLastIndex = userCode.lastIndexOf(targetCh);
        if(targetLastIndex != -1) {
            return userCode.substring(0,targetLastIndex) + '\n' + mainFunc + '\n' + userCode.substring(targetLastIndex);
        }
        throw new ServiceException(ResultCode.FAILED_CODE_STRUCTURE_ERROR);
    }


    /**
     * 获取判题结果
     */
    @CheckUserStatus
    public UserQuestionResVo exeResult(Long examId, Long questionId, String uniqueId) {
        Long userId = ThreadLocalUtil.get(UserConstants.USER_ID,Long.class);
        UserSubmit userSubmit = userSubmitMapper.selectSubmitRes(examId, questionId, uniqueId,userId);
        UserQuestionResVo resVo = new UserQuestionResVo();
        if(userSubmit == null) {
            // 还在判
            resVo.setPass(SubmitResType.IN_JUDGE.getValue());
        }else {
            resVo.setSubmitId(userSubmit.getSubmitId());
            resVo.setErrorMsg(userSubmit.getExeMessage());
            resVo.setPass(userSubmit.getPass());
            if(StrUtil.isNotEmpty(userSubmit.getCaseJudgeRes())) {
                resVo.setUserExeResItemList(JSON.parseArray(userSubmit.getCaseJudgeRes(), UserExeResItem.class));
            }
        }
        return resVo;
    }

}
